import { hex2Buffer, crc16 } from '@/utils';
import eventBus, { eventNames } from '@/utils/event-bus';
import { log, convertEndian } from '@/utils';
import { storage } from '@/modules/storage';

const blueTooth = window.ble;
const serviceUUID = 'fff0';
// 当前测试数据
const currentCode = '4245470F00000003000000000088EC';
// 开始测试
const startCode = '4245470F00000006000000000088B9';
// 停止测试
const stopCode = '4245470F0000000700000000008968';
// 历史数据
const historyCode = '42454711000000020002000000';
// 缺少数据区的仪器信息
const infoCodeLongHeader = '424547BA0200000500AB020000';
// 成功指令
const SUCCESS_CODE = '4245471000000004000100000001';
// 失败指令
const FAILURE_CODE = '4245471000000004000100000000';
// 请求仪器
const requestCode = '4245470F000000010000000000';
// 报文头
const MESSAGE_HEADER = '424547';
// 报文长度占用 4 字节数
const MESSAGE_LEN = 4;
let timer: NodeJS.Timeout | undefined = undefined;

/**
 * @description 向指定的 BLE 特征写入十六进制字符串格式的命令
 * @param {string} hex - 十六进制字符串
 */
export const writeHexCmd = (hex: string) => {
  if (!blueTooth) return;
  const mac = storage.get('mac');
  const writeCharUUID = storage.get('writeCharUUID');

  if (mac === null || writeCharUUID === null) {
    log('请先连接蓝牙', mac, writeCharUUID);
    return;
  }
  log(`准备写入，指令字: ${hex.slice(14, 16).padStart(4, '0')} 指令: ${hex}`);

  const data = hex2Buffer(hex);
  blueTooth.writeWithoutResponse(
    mac,
    serviceUUID,
    writeCharUUID,
    data.buffer,
    () => {
      log(`写入成功`);
    },
    err => {
      log('写入失败', err);
      eventBus.$emit(eventNames.writeHexCmdFailure, err);
    }
  );
};

/**
 * 向蓝牙设备写入获取当前试验数据指令
 */
export const writeCurrentCode = () => {
  writeHexCmd(currentCode);
};

/**
 * 向蓝牙设备写入开始测试指令
 */
export const writeStartCode = () => {
  writeHexCmd(startCode);
};

/**
 * 向蓝牙设备写入停止测试指令
 */
export const writeStopCode = () => {
  writeHexCmd(stopCode);
};

/**
 * 向蓝牙设备写入获取仪器信息指令
 */
export const writeInfoCode = () => {
  const repeatFF = 'FF'.repeat(683);
  const infoCodeLong = infoCodeLongHeader + repeatFF;
  const crc = crc16(infoCodeLong);
  const code = infoCodeLong + crc;
  // 分包发送，每包200个字节
  const chunkSize = 200 * 2;
  const chunks = Math.ceil(code.length / chunkSize);
  for (let i = 0; i < chunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, code.length);
    const chunk = code.slice(start, end);
    setTimeout(() => {
      writeHexCmd(chunk);
    }, i * 100);
  }
};

/**
 * 向蓝牙设备写入请求连接指令
 */
export const writeRequestCode = () => {
  const crc = crc16(requestCode);
  writeHexCmd(requestCode + crc);
  timer = setTimeout(() => {
    writeFailureCode();
    clearTimeout(timer);
  }, 10000);
};

/**
 * 向蓝牙模块写入成功指令
 */
export const writeSuccessCode = () => {
  const crc = crc16(SUCCESS_CODE);
  writeHexCmd(SUCCESS_CODE + crc);
};

/**
 * 向蓝牙模块写入失败指令
 */
export const writeFailureCode = () => {
  const crc = crc16(FAILURE_CODE);
  writeHexCmd(FAILURE_CODE + crc);
};

/**
 * 向蓝牙写入历史数据指令
 * @param historyNo 历史数据编号
 */
export const writeHistoryCode = (historyNo: string) => {
  const matched = parseInt(historyNo).toString(16).toUpperCase().padStart(4, '0');
  const dataHex = convertEndian(matched);
  const hex = historyCode + dataHex;
  writeHexCmd(hex + crc16(hex));
};

/**
 * 初始化 BLE 蓝牙通知
 */
export const startNotification = (callback: (res: ArrayBuffer) => void) => {
  if (!blueTooth) return;
  const mac = storage.get('mac');
  const notifyCharUUID = storage.get('notifyCharUUID');

  if (mac === null || notifyCharUUID === null) {
    console.log('请先连接蓝牙', mac, notifyCharUUID);
    return;
  }
  blueTooth.startNotification(
    mac,
    serviceUUID,
    notifyCharUUID,
    res => {
      log('收到通知：', res);
      callback(res);
    },
    err => {
      console.error('开始通知失败', err);
      // reject(err);
    }
  );
  // return new Promise<ArrayBuffer>((resolve, reject) => {
  //   if (!blueTooth) return;
  //   const mac = storage.get('mac');
  //   const notifyCharUUID = storage.get('notifyCharUUID');

  //   if (mac === null || notifyCharUUID === null) {
  //     console.log('请先连接蓝牙', mac, notifyCharUUID);
  //     return;
  //   }
  //   blueTooth.startNotification(
  //     mac,
  //     serviceUUID,
  //     notifyCharUUID,
  //     res => {
  //       console.log('开始通知成功', res);
  //       resolve(res);
  //     },
  //     err => {
  //       console.error('开始通知失败', err);
  //       reject(err);
  //     }
  //   );
  // });
};

/**
 * 断开蓝牙连接
 */
export const disconnect = () => {
  if (!blueTooth) return;
  const mac = storage.get('mac');
  if (mac === null) {
    console.log('请先连接蓝牙', mac);
    return;
  }
  blueTooth.disconnect(
    mac,
    () => {
      console.log('断开蓝牙成功');
    },
    err => {
      console.log('断开蓝牙失败', err);
    }
  );
};

/**
 * 判断是否开启蓝牙
 */
export const isEnabled = (): Promise<void> => {
  return new Promise((resolve, reject) => {
    if (!blueTooth) reject('未检测到蓝牙');
    // 进入判断是否开启蓝牙
    blueTooth.isEnabled(
      () => {
        console.log('蓝牙已开启');
        resolve(void 0);
      },
      error => {
        console.log('蓝牙打开失败:', error);
        reject(error);
      }
    );
  });
};

/**
 * 停止蓝牙通知
 */
export const stopNotify = () => {
  return new Promise<void>((resolve, reject) => {
    if (!blueTooth) return;
    const mac = storage.get('mac');
    const notifyCharUUID = storage.get('notifyCharUUID');
    if (mac === null || notifyCharUUID === null) {
      console.log('请先连接蓝牙', mac);
      return;
    }
    blueTooth.stopNotification(
      mac,
      serviceUUID,
      notifyCharUUID,
      () => {
        console.log('停止通知成功');
        resolve();
      },
      err => {
        console.log('停止通知失败', err);
        reject(err);
      }
    );
  });
};

/**
 * 监听蓝牙变更状态, turningOff, off, turningOn, on
 */
export const startStateNotifications = (): Promise<string> => {
  return new Promise(resolve => {
    if (!blueTooth) return;
    blueTooth.startStateNotifications(state => {
      resolve(state);
    });
  });
};

/**
 * 开启蓝牙
 */
export const enable = () => {
  return new Promise<void>((resolve, reject) => {
    if (!blueTooth) return;
    blueTooth.enable(
      () => {
        console.log('成功开启蓝牙');
        resolve();
      },
      err => {
        console.log('打开蓝牙失败', err);
        reject(err);
      }
    );
  });
};

/**
 * 开始扫描蓝牙
 */
export const startScanWithOptions = (callback: (device: BLECentralPlugin.PeripheralData) => void) => {
  if (!blueTooth) return;
  blueTooth.startScanWithOptions(
    [],
    { scanMode: 'lowLatency' },
    device => {
      callback(device);
    },
    err => {
      console.log('扫描蓝牙失败', err);
    }
  );
  // return new Promise<BLECentralPlugin.PeripheralData>((resolve, reject) => {
  //   if (!blueTooth) return;
  //   blueTooth.startScanWithOptions([], { scanMode: 'lowLatency' }, (device) => {
  //     resolve(device);
  //   }, (err) => {
  //     console.log('扫描蓝牙失败', err);
  //     reject(err);
  //   })
  // })
};

/**
 * 停止蓝牙扫描
 */
export const stopScan = () => {
  return new Promise<void>((resolve, reject) => {
    if (!blueTooth) return;
    blueTooth.stopScan(
      () => {
        resolve();
      },
      () => {
        console.log('停止蓝牙扫描失败');
        reject();
      }
    );
  });
};

/**
 * 自动连接蓝牙设备
 * @param id 蓝牙设备 id
 */
export const autoConnect = (id: string) => {
  return new Promise<BLECentralPlugin.PeripheralDataExtended>((resolve, reject) => {
    if (!blueTooth) return;
    blueTooth.autoConnect(
      id,
      res => {
        console.log('蓝牙连接成功', res);
        resolve(res);
      },
      err => {
        console.log('蓝牙连接失败', err);
        eventBus.$emit(eventNames.bluetoothDisconnect, err);
        reject(err);
      }
    );
  });
};

/**
 * 获取正确的数据，蓝牙数据存在混淆或不完整的情况
 * @param hex 蓝牙接受到的数据
 */
const getRightHex = (hex: string) => {
  // 一个 16 进制字符占用 4 位，一个字节占用 8 位，所以 1 字节 = 2 个 16 进制字符
  // | 报文头 | 报文长度 | 命令字 | 数据长度 | 数据 | 校验码 |
  // |   6   |   8    |   4   |    8    |  *  |   4   |  -- 16 进制字符长度
  // |   3   |   4    |   2   |    4    |  *  |   2   |  -- 16 字节数

  // 若存在粘包的情况，即数据包含两个报文头，解析前一个报文内容
  if (hex.startsWith(MESSAGE_HEADER, MESSAGE_HEADER.length)) {
    const result = hex.slice(0, hex.indexOf(MESSAGE_HEADER, MESSAGE_HEADER.length));
    return getRightHex(result);
  }

  // 由于报文可能被截断，需要从之前的数据中获取报文长度
  let len = 0;

  // 若以报文头开头的话，则获取报文长度，否则拼接 localStorage 的数据
  if (hex.startsWith(MESSAGE_HEADER)) {
    const lenStr = convertEndian(hex.slice(MESSAGE_HEADER.length, MESSAGE_HEADER.length + MESSAGE_LEN * 2));
    if (lenStr) len = parseInt(lenStr, 16);
  } else {
    const sessionHex = storage.get('hex') ?? '';
    const sessionHexLength = storage.get('hex-length') ?? '';
    hex = sessionHex + hex;
    len = Number(sessionHexLength);
  }

  // 长度符合要求，则返回数据，不符合要求，则保存数据到 localStorage
  if (len && hex.length === len * 2) {
    storage.remove('hex');
    storage.remove('hex-length');
    return hex;
  } else {
    storage.set('hex', hex);
    storage.set('hex-length', len);
    return false;
  }
};

// TODO: 数据解析的不应该放置蓝牙模块
/**
 * 处理从蓝牙设备接收的十六进制字符串，验证其完整性，并且根据指定的命令类型发出事件
 */
export const handleResHex = (_h: string) => {
  const hex = getRightHex(_h);
  if (hex === false) return;

  // 数据接收完毕
  const resHexCmd = convertEndian(hex.slice(14, 18));

  if (timer) {
    clearTimeout(timer);
  }

  if (['0002', '0003'].includes(resHexCmd)) {
    // CRC校验
    const crcDataHex = hex.slice(0, -4);
    const chkCrc16 = crc16(crcDataHex);
    if (hex.slice(-4) !== chkCrc16) {
      log('数据校验失败');
      setTimeout(() => {
        writeFailureCode();
      }, 300);
      return;
    } else {
      setTimeout(() => {
        writeSuccessCode();
      }, 300);
    }
  }
  const hexDataLenString = convertEndian(hex.slice(18, 26));
  const hexDataLen = parseInt(hexDataLenString, 16);
  const hexData = hex.slice(26, hexDataLen * 2 + 26);
  console.log(`数据区：${hexData}`);

  if (resHexCmd === '0003' || resHexCmd === '0002') {
    // 0002：历史数据 0003：当前测试数据  回复指令
    eventBus.$emit(eventNames.receiveData, hexData);
  } else if (resHexCmd === '0006') {
    // 开始测试 回复指令, 1=空闲,成功，2=忙碌，失败
    const state = hex.slice(28, 30);
    eventBus.$emit(eventNames.startTestState, state);
  } else if (resHexCmd === '0007') {
    // 暂停测试 回复指令，1=空闲，成功，2=忙碌，失败
    const state = hex.slice(28, 30);
    console.log('暂停测试', state);
    eventBus.$emit(eventNames.receiveStop, state);
  } else if (resHexCmd === '0005') {
    const baseInfoData = hex.slice(26, -4);
    // 获取设备信息
    eventBus.$emit(eventNames.baseInfo, baseInfoData);
  } else if (resHexCmd === '0001') {
    // 握手指令
    const code = hex.slice(28, 30);
    eventBus.$emit(eventNames.handShake, code);
  }
};

export const requestMtu = (gMtuSize: number) => {
  return new Promise<void>((resolve, reject) => {
    if (!blueTooth) return;
    const mac = storage.get('mac');
    if (mac === null) {
      console.log('请先连接蓝牙', mac);
      return;
    }
    blueTooth.requestMtu(
      mac,
      gMtuSize,
      () => {
        console.log('响应 MTU');
        resolve();
      },
      () => {
        console.error('Failed to request MTU.');
        reject();
      }
    );
  });
};
